import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.MatteBorder;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.util.Random;

public class MazeGameBoard extends JFrame implements AWTEventListener {

	private Boolean canMove = true;
	private GameClient gclient;
	private GameInfoPanel gip;
	private MazeGrid mg;
	private boolean endGame = false;

	public MazeGameBoard(final int nsize, GameClient cli) {
		gclient = cli;
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setTitle("Player " + cli.getID());
		this.setLayout(new BorderLayout());
		mg = new MazeGrid(nsize, cli.initPosition);
		this.add(mg, BorderLayout.WEST);
		gip = new GameInfoPanel();
		this.add(gip, BorderLayout.EAST);
		this.pack();
		gip.setSize(this.getSize());
		this.setLocationRelativeTo(null);
		this.setVisible(true);
		this.getToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
		
		Random random = new Random();
		this.setLocation(random.nextInt(500), random.nextInt(500));
		SetCanMove(true);
	}
	
	public void endGame(HashMap <Integer, Integer> finalret) {
		System.out.println("End game");
		this.getToolkit().removeAWTEventListener(this);
		gip.updateInfoPanel(finalret);
		gip.displayError("GAME END");		
//		JOptionPane.showMessageDialog(null, "Game end");
	}
	
	public void setEndGame() {
		endGame = true;
	}

	public void updateGameBoard(PlayerMovementInfo playersInfo) {
//		System.out.println("Update game board ");
		if (playersInfo.moveSuccess) {
			// render maze gird
			mg.updateGrid(playersInfo.playerMapCoordinates, playersInfo.remainingTreasureCoordinates, gclient.getID());
			//render info panel
			gip.updateInfoPanel(playersInfo.playerTreasureCount);
		} else {
//			mg.updateGrid(playersInfo.playerMapCoordinates, gclient.getID());
			gip.displayError(playersInfo.ServerMsg);
			
		}
		if (endGame) {
			endGame(playersInfo.playerTreasureCount);
		}
		SetCanMove(true);
//		System.err.println("updateGameBoard Can move");
	}

	public class GameInfoPanel extends JPanel {
		private JTextPane playerTreasures;
		private JTextArea errorMessage;

		public GameInfoPanel() {
			this.setLayout(new BorderLayout());
			playerTreasures = new JTextPane();
			playerTreasures.setText("Treasures Stat");
			playerTreasures.setEditable(false);
			add(playerTreasures,BorderLayout.NORTH);
			errorMessage = new JTextArea();
			errorMessage.setForeground(Color.RED);
			errorMessage.setLineWrap(true);
			errorMessage.setEditable(false);

			add(errorMessage, BorderLayout.SOUTH);
		}
		
		public void updateInfoPanel(HashMap<Integer, Integer> treasureCount){
			System.out.println("Update Info Panel");
			int index;
			String s = new String("Treasures Stat\n");
			for (Map.Entry<Integer, Integer> entry : treasureCount.entrySet()) {
				s = s.concat("Player " + entry.getKey() + ": " + entry.getValue()+"\n");
				System.out.println("Player " + entry.getKey() + " has " + entry.getValue() + " treasures");
			}
			playerTreasures.setText(s);
			errorMessage.setText("");
		}
		
		public void endGame() {
			String s = playerTreasures.getText();
			playerTreasures.setText("END GAME\n" + s);
		}
		
		public void displayError(String msg){
			errorMessage.setText(msg);
		}
	}

	public class MazeGrid extends JPanel {
		private int nsize;

		public MazeGrid(int nsize, Coordinates pos) {
			setLayout(new GridBagLayout());
			this.nsize = nsize;
			GridBagConstraints gbc = new GridBagConstraints();
			for (int row = 0; row < nsize; row++) {
				for (int col = 0; col < nsize; col++) {
					gbc.gridx = col;
					gbc.gridy = row;

					CellPane cellPane = new CellPane();
					Border border = null;
					if (row == pos.y && col == pos.x) {
						border = new MatteBorder(1, 1, 1, 1, Color.GRAY);
						cellPane.setBorder(border);
						cellPane.setBackground(Color.RED);
						add(cellPane, gbc);
						continue;
					}
					if (row < 4) {
						if (col < 4) {
							border = new MatteBorder(1, 1, 1, 1, Color.GRAY);
						} else {
							border = new MatteBorder(1, 1, 1, 1, Color.GRAY);
						}
					} else {
						if (col < 4) {
							border = new MatteBorder(1, 1, 1, 1, Color.GRAY);
						} else {
							border = new MatteBorder(1, 1, 1, 1, Color.GRAY);
						}
					}
					cellPane.setBorder(border);
					add(cellPane, gbc);
				}
			}
		}

		public void updateGrid(HashMap<Integer, Coordinates> coords,  HashMap<Integer, HashMap<Integer, Integer>> treasures, int myID) {
			Color defaultColor = UIManager.getColor("Panel.background");
			for (Component com : getComponents()) {
				((CellPane)com).clear();
			}

			int index;
			for (Map.Entry<Integer, Coordinates> entry : coords.entrySet()) {
				System.out.println("Player " + entry.getKey() + " at ("
						+ entry.getValue().x + "," + entry.getValue().y + ")");
				index = (entry.getValue().y) * nsize + entry.getValue().x;
				CellPane cell = (CellPane) getComponent(index);
				cell.setLabel("P" + entry.getKey());				
				if (entry.getKey() == myID) {					
					getComponent(index).setBackground(Color.RED);
					
				} else {
					getComponent(index).setBackground(Color.BLUE);
				}
			}
			
			//update remaining treasures
			for (Map.Entry<Integer, HashMap<Integer, Integer>> entryMap : treasures.entrySet()) {
				int x = entryMap.getKey();
				HashMap<Integer, Integer> yMap = entryMap.getValue();
				for (Map.Entry<Integer, Integer> entry : yMap.entrySet()) {
					int y = entry.getKey();
					index = y * nsize + x;
					//TODO: array index out of bound
					CellPane cell = (CellPane) getComponent(index);
					cell.setTreasures(entry.getValue());
				}
			}
		}
	}

	public class CellPane extends JPanel {

		private Color defaultBackground;
		private JLabel label;
		private JLabel noTreasures;

		public CellPane() {
			defaultBackground = UIManager.getColor("Panel.background");
			label = new JLabel("");
			noTreasures = new JLabel("");
			this.setLayout(new BorderLayout());
			this.add(label, BorderLayout.CENTER);
			this.add(noTreasures, BorderLayout.SOUTH);
		}

		@Override
		public Dimension getPreferredSize() {
			return new Dimension(50, 50);
		}
		
		public void setLabel(String s) {
			label.setText(s);
		}
		
		public void clear() {
			label.setText("");
			setBackground(defaultBackground);
		}
		
		public void setTreasures(int n) {
			if (n==0) {
				noTreasures.setText("");
			} else {
				noTreasures.setText(Integer.toString(n));
			}
		}
		
		public void resetNumberTreasures() {
			noTreasures.setText("0");
		}
	}

	@Override
	public void eventDispatched(AWTEvent event) {
		
		
		if (event instanceof KeyEvent) {
			KeyEvent key = (KeyEvent) event;
			if (key.getID() == KeyEvent.KEY_RELEASED) { // Handle key presses
				if (!canMove) {
					System.err.println("keyReleased Event - cannot move");
					return;
				}
				
				SetCanMove(false);
				System.err.println("eventDispatched: Can not move");
				switch (key.getKeyCode()) {
				case KeyEvent.VK_SPACE:
					System.out.println("Space key released");
					gclient.move(PlayerMovementInfo.Direction.NO_MOVE);
					break;
				case KeyEvent.VK_UP:
					System.out.println("Up key released");
					gclient.move(PlayerMovementInfo.Direction.DIR_UP);
					break;
				case KeyEvent.VK_DOWN:
					System.out.println("Down key released");
					gclient.move(PlayerMovementInfo.Direction.DIR_DOWN);
					break;
				case KeyEvent.VK_LEFT:
					System.out.println("Left key released");
					gclient.move(PlayerMovementInfo.Direction.DIR_LEFT);
					break;
				case KeyEvent.VK_RIGHT:
					System.out.println("Right key released");
					gclient.move(PlayerMovementInfo.Direction.DIR_RIGHT);
					break;
				default:
					SetCanMove(true);
					break;
				}
				key.consume();
			}
		}
	}
	
	public synchronized void SetCanMove (boolean movePerm) {
		canMove = movePerm;
	}
}